/**
* 裸体指针？ raw pointer ,这么翻译并不好，也许翻译为原始指针更好一些
* 一、原始指针的定义
*  *const T  -- 不可变原始指针
*  *mut T    -- 可变原始指针
*   可以在安全代码中 创建 裸指针，只是不能在不安全块之外 解引用 裸指针
*
* 二、比较原始指针和智能指针
   允许忽略借用规则，可以同时拥有不可变和可变的指针，或多个指向相同位置的可变指针
   不保证指向有效的内存
   允许为空
   不能实现任何自动清理功能
*/
//Rust提供的用于处理C字符串的类型。CString::new用于创建一个新的C字符串。它确保字符串以空字符（\0）结尾，这是C字符串的要求
use std::slice;
use std::panic;

#[derive(Debug)]
struct Person{
    name: String,
    age: u8,
}

#[derive(Debug)]
struct Food{
    weight: u8,
}

fn main() {
    //1.0 演示原始指针的创建和访问，以及可以同时拥有多个指向相同位置的原始指针(不论是可变还是不可变的原始指针)
    // 以及如何解引用原始指针
    let mut num = 5; //这里必须定义为mut 。但也发现了rust编译器的一个问题：会错误体提示要移除这个mut,但其实不能移除。
    let r1 = &num as *const i32; // 不可变原始指针
    let r2 = &mut num as *mut i32; // 可变原始指针
    println!("r1: {}, r2: {}", unsafe { *r1 }, unsafe { *r2 });
    //让r2+1
    unsafe {
        *r2 += 1;
    }
    println!("r1: {}, r2: {}", unsafe { *r1 }, unsafe { *r2 });

    let p=Person{name: String::from("Rust"), age: 20};
    let ptr = &p as *const Person;
    println!("{:?}", unsafe { &(*ptr)});
    let agg=Food{weight: 10};
    let ptr2 = &agg as *const Food;
    println!("{:?}", unsafe { &(*ptr2)});

    //2.0   创建指向任意内存地址的原始指针
    // 此处代码需要注释掉，否则后续的代码不会执行。因为原始指针指向的内存地址是不合法的，所以会引发运行时错误。
    //create_raw_pointer_from_address();

    println!("---------------------------------------------------------------");

    //3.0 使用Box创建原始指针
    create_raw_pointer_use_box();

    //4.0 如何创建一个空的原始指针
    create_empty_raw_pointer();

    //5.0 演示如果使用原始指针把一个数组切成两半，且每一半都是可变的切片
    demo_split_slice_to_two_half();
}


/**
 * 创建一个指向任意内存地址的原始指针
 */
#[allow(dead_code)]
fn create_raw_pointer_from_address() {
    let address = 0x012345usize;
    let r = address as *const i32;
    unsafe {
        println!("r: {}", *r);
    }
}
/**
 * 使用Box创建原始指针
 */
fn create_raw_pointer_use_box() {
    //回忆下Box指针，我们知道Box指针是一个堆上分配的智能指针。
    let boxed = Box::new(5);
    let five = *boxed;
    println!("five: {}", five);

    let brave = Box::new(String::from("Rust"));
    let raw_brave = Box::into_raw(brave);
    unsafe{
        println!("raw_brave: {:?}", *raw_brave);
    }

    //现在需要手动释放内存，否则会造成内存泄露
    println!("释放raw_brave");
    unsafe{
        drop(Box::from_raw(raw_brave));
    }
    println!("释放raw_brave完成");
    // 以下代码需要注释掉，否则后续的代码不会执行。因为原始指针指向的内存地址是不合法的，可能会引发运行时错误。
    // 很可惜，就是使用panic::catch_unwind,页无法阻止程序退出
    // 虽然它会提示  thread 'main' panicked at std\src\io\stdio.rs:1123:9
    unsafe{
        let result = panic::catch_unwind(|| {
            println!("raw_brave依然存在，但这个时候它应该是一个空的: {:?}", *raw_brave);
        });
        match result {
            Ok(_) => println!("一切正常"),
            Err(_) => println!("在访问有问题的原始指针后，程序出问题了"),
        }
        println!("--------------------- 继续运行  ----------------------- ");
    }
}


/**
 * 创建一个空的原始指针-利用std::ptr::null
 */
fn create_empty_raw_pointer() {
    let mut ptr = std::ptr::null::<i32>();
    if ptr.is_null() {
        println!("指针ptr是空的");
    } else {
        println!("指针ptr不是空的");
    }

    let mut value = 5;
    unsafe {
        // 将指针指向一个有效的内存地址
        ptr = &value as *const i32;
        // 读取指针的值
        if !ptr.is_null() {
            println!("指针ptr现在的值是: {}", *ptr);
        } else {
            println!("指针ptr现在是空的");
        }
    }
}

/**
 * 演示如果使用原始指针把一个数组切成两半，且每一半都是可变的切片
 */
fn demo_split_slice_to_two_half() {
    let mut scores=[10,20,30,40,50];
    println!("原始scores: {:?}", scores);
    let (left, right) = unsafe_slice(&mut scores, 3);
    println!("scores-left: {:?}, scores-right: {:?}", left, right);
    //现在改改左边第一个，看是不是发生了效果
    left[0] = 1024;
    right[0] = 1975;
    println!("修改后scores: {:?}", scores);  //事实证明了这点：左边改了，所以可变切片可以的
    let mut poems=["独怜幽草涧边生","上有黄丽深树鸣","春潮带雨晚来急"];
    let (left, right) = unsafe_slice(&mut poems, 2);
    println!("left: {:?}, right: {:?}", left, right);
}


/**
 * 使用原始指针创建切片
 * 这里有非常关键的说明:(来自书本)
 * Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同部分：它只知道我们借用了同一个 slice 两次。
 * 本质上借用 slice 的不同部分是可以的，因为结果两个 slice 不会重叠，不过 Rust 还没有智能到能够理解这些
 * 
 * 这一段至少说明了：rust的编译器也不是万能的，要是万能就太慢了。
 * 在rust书中，此例子属于：创建不安全代码的安全抽象  。
 * 即把不安全的操作封装起来，对外提供安全的接口（函数）
 * @param values 实际是一个数组的完整切片
 * @param mid 切片中间的索引位置
 */
fn unsafe_slice<T>(values: &mut [T], mid: usize)-> (&mut [T], &mut [T]) {
    //let mut data = [1, 2, 3];
    //let ptr = &data as *const _;
    let len = values.len();
    let ptr = values.as_mut_ptr();   // 切片转为原始指针，且指向切片的起始位置。

    //方法from_raw_parts_mut用于从原始指针和长度创建一个可变切片。
    //注意：这里的add方法，是原始指针的一个方法，用于计算偏移后的新地址。
    //如果切片中保存的是汉字，那么应该加多少了？    
    unsafe {
        (
            slice::from_raw_parts_mut(ptr, mid),
            slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}
